package com.zhongen.wflow.workflow.task;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zhongen.wflow.bean.entity.WflowModelHistorys;
import com.zhongen.wflow.mapper.WflowModelHistorysMapper;
import com.zhongen.wflow.utils.BeanUtil;
import com.zhongen.wflow.utils.EmailUtil;
import com.zhongen.wflow.workflow.bean.dto.ProcessInstanceOwnerDto;
import com.zhongen.wflow.workflow.bean.process.props.TriggerProps;
import com.zhongen.wflow.workflow.config.WflowGlobalVarDef;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;

import jakarta.mail.MessagingException;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.stream.Collectors;

/**
 * 触发器任务
 *
 * @author : willian fu
 * @date : 2022/9/12
 */
@Slf4j
public class TriggerServiceTask implements JavaDelegate {

	public static RuntimeService runtimeService;

	public static EmailUtil emailUtil;

	public static WflowModelHistorysMapper historysMapper;

	public TriggerServiceTask() {
		runtimeService = BeanUtil.getBean(RuntimeService.class);
		emailUtil = BeanUtil.getBean(EmailUtil.class);
		historysMapper = BeanUtil.getBean(WflowModelHistorysMapper.class);
	}

	@Override
	public void execute(DelegateExecution execution) {
		FlowElement element = execution.getCurrentFlowElement();
		Map nodeProps = execution.getVariable(WflowGlobalVarDef.WFLOW_NODE_PROPS, Map.class);
		Optional.ofNullable(nodeProps.get(element.getId())).ifPresent(props -> {
			TriggerProps triggerProps = (TriggerProps) props;
			if (TriggerProps.TriggerTypeEnum.WEBHOOK.equals(triggerProps.getType())) {
				TriggerProps.Http http = triggerProps.getHttp();
				try {
					doRequest(execution, http);
				}
				catch (Exception e) {
					log.error("触发器[{}]请求[{}]异常,{}", element.getId(), http.getUrl(), e.getMessage());
				}
				log.info("触发器[{}]发送请求[{}]", element.getId(), http.getUrl());
			}
			else if (TriggerProps.TriggerTypeEnum.EMAIL.equals(triggerProps.getType())) {
				TriggerProps.Email email = triggerProps.getEmail();
				doSendEmail(execution, email);
				log.info("触发器[{}]发送邮件[{}]", element.getId(), email.getTo());
			}
		});
	}

	/**
	 * 发送http请求
	 * @param execution 上下文
	 * @param http http配置
	 */
	private void doRequest(DelegateExecution execution, TriggerProps.Http http) {
		Map<String, Object> variables = getContextVar(execution);
		String url = getStrByRegex(http.getUrl(), variables);
		HttpRequest request;
		switch (http.getMethod()) {
			case "POST":
				request = HttpRequest.post(url);
				break;
			case "PUT":
				request = HttpRequest.put(url);
				break;
			case "DELETE":
				request = HttpRequest.delete(url);
				break;
			default:
				request = HttpRequest.get(url);
				break;
		}
		// 处理请求头
		http.getHeaders().forEach(hd -> {
			request.header(hd.getName(),
					hd.getIsField() ? String.valueOf(variables.getOrDefault(String.valueOf(hd.getValue()), ""))
							: getStrByRegex(String.valueOf(hd.getValue()), variables));
		});
		// 处理请求体
		if ("FORM".equals(http.getContentType())) {
			http.getParams().forEach(hd -> {
				request.form(hd.getName(),
						hd.getIsField() ? String.valueOf(variables.getOrDefault(String.valueOf(hd.getValue()), ""))
								: getStrByRegex(String.valueOf(hd.getValue()), variables));
			});
		}
		else {
			request.body(
					JSONObject.toJSONString(http.getParams()
						.stream()
						.collect(Collectors.toMap(TriggerProps.Http.Variable::getName,
								v -> v.getIsField() ? variables.getOrDefault(String.valueOf(v.getValue()), "")
										: getStrByRegex(String.valueOf(v.getValue()), variables)))),
					"application/json");
		}
		HttpResponse response = request.timeout(10000).executeAsync();
		if (response.isOk()) {
			log.info("审批实例[{}]触发器发送请求成功[{} {}] 返回[{}]", execution.getProcessInstanceId(), http.getMethod(),
					http.getUrl(), response.body());
		}
		else {
			log.warn("审批实例[{}]触发器发送请求失败[{} {}]", execution.getProcessInstanceId(), http.getMethod(), http.getUrl());
		}
	}

	/**
	 * 发送邮件
	 * @param execution 上下文
	 * @param email email配置
	 */
	private void doSendEmail(DelegateExecution execution, TriggerProps.Email email) {
		Map<String, Object> variables = getContextVar(execution);
		// 模板解析标题
		String subject = getStrByRegex(email.getSubject(), variables);
		// 模板解析内容
		String content = getStrByRegex(email.getContent(), variables);
		try {
			emailUtil.sendHtmlMail(subject, content, email.getTo().toArray(new String[0]));
		}
		catch (MessagingException e) {
			log.error("发送邮件给{}失败[标题：{}] [内容 {}]", email.getTo(), subject, content);
		}
		// TODO 执行邮件发送
		log.info("发送邮件给{}[标题：{}] [内容 {}]", email.getTo(), subject, content);
	}

	/**
	 * 构建用于模板替换的变量
	 * @param execution 执行实例
	 * @return 变量map
	 */
	private Map<String, Object> getContextVar(DelegateExecution execution) {
		Map<String, Object> variables = execution.getVariables();
		WflowModelHistorys models = historysMapper
			.selectOne(new LambdaQueryWrapper<WflowModelHistorys>().select(WflowModelHistorys::getFormName)
				.eq(WflowModelHistorys::getProcessDefId, execution.getProcessDefinitionId()));
		variables.put("formName", Objects.nonNull(models) ? models.getFormName() : "--");
		variables.put("instanceId", execution.getProcessInstanceId());
		ProcessInstanceOwnerDto owner = (ProcessInstanceOwnerDto) variables.get("owner");
		variables.put("owner.id", owner.getOwner());
		variables.put("owner.name", owner.getOwnerName());
		variables.put("owner.deptId", owner.getOwnerDeptId());
		variables.put("owner.deptName", owner.getOwnerName());
		return variables;
	}

	/**
	 * 解析模板
	 * @param str 模板字符串
	 * @param params 替换的变量
	 * @return 解析后的内容
	 */
	public static synchronized String getStrByRegex(String str, Map<String, Object> params) {
		StringBuffer sb = new StringBuffer();
		Matcher m = WflowGlobalVarDef.TEMPLATE_REPLACE_REG.matcher(str);
		while (m.find()) {
			String key = m.group();
			String value = String.valueOf(params.get(key.substring(2, key.length() - 1).trim()));
			m.appendReplacement(sb, value == null ? "" : value);
		}
		m.appendTail(sb);
		return sb.toString();
	}

}
